package finder

import (
	"binseek/pkg/utils"
	"bytes"
	"fmt"
	"io"
	"sort"
)

type ReaderIndex interface {
	Count() int
	ReadIndex(idx, size int) ([]byte, error)
}

func BinSearch(r ReaderIndex, target []byte, isBom, checkFloor, checkCeiling bool) int {
	find := func(i int) bool {
		key, _ := r.ReadIndex(i, len(target))
		return utils.Compare(target, key, isBom) < 0
	}
	if checkFloor && find(0) {
		return -1 // 超出下限
	}
	if checkCeiling && !find(-1) {
		return -2 // 超出上限
	}
	return sort.Search(r.Count(), find) - 1
}

type Glossary struct {
	data []byte
	Dict map[int][]byte
}

func CreateGlossary(data []byte) Glossary {
	lst := bytes.SplitAfter(data, []byte{0x00})
	sort.Slice(lst, func(i, j int) bool {
		return bytes.Compare(lst[i], lst[j]) <= 0
	})
	dix := Glossary{Dict: make(map[int][]byte)}
	for i, elem := range lst {
		dix.data = append(dix.data, elem...)
		dix.Dict[i] = elem
	}
	return dix
}

func (g Glossary) Count() int {
	return len(g.Dict)
}

func (g Glossary) Bytes() []byte {
	return g.data
}

func (g Glossary) ReadIndex(idx, size int) ([]byte, error) {
	if idx < 0 || idx >= g.Count() {
		return nil, fmt.Errorf("index is out of range")
	}
	return g.Dict[idx], nil
}

type Catalog struct { // 目录
	fixSize, idxCount int
	isMmap            bool
	Data              []byte
	io.ReaderAt
	ReaderIndex
}

func NewCatalog(fixSize int) *Catalog {
	return &Catalog{fixSize: fixSize}
}

func (c *Catalog) Count() int {
	if c.idxCount <= 0 && c.fixSize > 0 {
		c.idxCount = len(c.Data) / c.fixSize
	}
	return c.idxCount
}

func (c *Catalog) ReadIndex(idx, size int) ([]byte, error) {
	if idx < 0 {
		idx += c.Count()
	}
	data, offset := make([]byte, size), int64(c.fixSize*idx)
	n, err := c.ReadAt(data, offset)
	return data[:n], err
}

func (c *Catalog) ReadAt(data []byte, offset int64) (int, error) {
	start := int(offset)
	stop := start + len(data)
	return copy(data[:], c.Data[start:stop]), nil
}
